package com.yuyou.fn.educational.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.github.wens.framework.service.impl.SuperServiceImpl;
import com.github.wens.mybatisplus.examples.Example;
import com.yuyou.fn.common.exception.BusException;
import com.yuyou.fn.common.util.BeanUtils;
import com.yuyou.fn.common.util.TimeUtils;
import com.yuyou.fn.common.util.Utils;
import com.yuyou.fn.educational.entity.*;
import com.yuyou.fn.educational.mapper.OnlineTestMapper;
import com.yuyou.fn.educational.service.*;
import com.yuyou.fn.educational.vo.ExamConfigVo;
import com.yuyou.fn.educational.vo.ExamPaperQuestionsVo;
import com.yuyou.fn.educational.vo.StudentMeasureScoreSaveOrUpdateVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
 *
 * OnlineTest 表数据服务层接口实现类
 *
 */
@Service
public class OnlineTestServiceImpl extends SuperServiceImpl<OnlineTestMapper, OnlineTest,Long> implements IOnlineTestService {

    @Resource
    private IMeasurementService measurementService;

    @Resource
    private ICampusService campusService;

    @Resource
    private IOnlineTestResultService onlineTestResultService;

    @Resource
    private IExamPaperService examPaperService;

    @Resource
    private IStudentMeasureScoreService studentMeasureScoreService;

    @Resource
    private IExternalMeasureScoreService externalMeasureScoreService;

    @Resource
    private IStudentService studentService;


    @Transactional(propagation = Propagation.REQUIRES_NEW )
    @Override
    public OnlineTest generateOnlineTestForStudent(Long campusId, Long studentId, Long measurementId, int asFlag) {
        Measurement measurement = measurementService.findById(measurementId);
        OnlineTest onlineTest = new OnlineTest();
        onlineTest.setCampusId(campusId);
        onlineTest.setCampusName(campusService.findCampusNameByCampusId(campusId));
        onlineTest.setGradeId(measurement.getGradeId());
        onlineTest.setGradeName(measurement.getGradeName());
        onlineTest.setSubjectId(measurement.getSubjectId());
        onlineTest.setSubjectName(measurement.getSubjectName());
        onlineTest.setCreateTime(TimeUtils.now());
        onlineTest.setUpdateTime(TimeUtils.now());
        onlineTest.setMeasurementId(measurementId);
        onlineTest.setStudentId(studentId);
        onlineTest.setAsFlag(asFlag);
        onlineTest.setExternalScoreType(measurement.getExternalScoreType());

        if(StringUtils.isEmpty(measurement.getExamConfig())){
            throw new BusException("请到线下校区学前诊断");
        }

        ExamConfigVo examConfigVo = JSONObject.parseObject(measurement.getExamConfig(),ExamConfigVo.class);

        if(examConfigVo.size() == 0 ){
            throw new BusException("请到线下校区学前诊断");
        }

        List<Long> allExamPaperIds = BeanUtils.toList(examConfigVo,"examPaperId");
        List<ExamPaper> examPapers = examPaperService.findByIds(allExamPaperIds,"asFlag");
        List<Long> paperIds = new ArrayList<>(examConfigVo.size());
        for(ExamPaper examPaper : examPapers ){
            if(onlineTest.getAsFlag() != 0 && !onlineTest.getAsFlag().equals(examPaper.getAsFlag())){
                continue;
            }
            paperIds.add(examPaper.getExamPaperId());
        }

        if(paperIds.isEmpty()){
            throw new BusException("请到线下校区学前诊断");
        }

        onlineTest.setAvailableExamPaperIds(StringUtils.join(paperIds,","));
        try{
            insertSelective(onlineTest);
        }catch (Exception e ){
            Example<OnlineTest> example = OnlineTest.createExample();
            example.createCriteria().andEqualTo("studentId" , studentId ).andEqualTo("campusId" , campusId)
                    .andEqualTo("measurementId" ,measurementId ).andEqualTo("asFlag" ,asFlag );
            onlineTest = findOneByExample(example);
        }
        return onlineTest;
    }

    @Override
    public List<OnlineTestResult> findOnlineTestResults(Long onlineTestId) {
        Example<OnlineTestResult> example = OnlineTestResult.createExample();
        example.createCriteria().andEqualTo("onlineTestId" , onlineTestId );
        example.orderBy("createTime").desc();
        return onlineTestResultService.findListByExample(example);
    }

    @Override
    public List<OnlineTestResult> findOnlineTestResults(List<Long> onlineTestIds) {
        Example<OnlineTestResult> example = OnlineTestResult.createExample();
        example.createCriteria().andIn("onlineTestId" , onlineTestIds );
        example.orderBy("createTime");
        return onlineTestResultService.findListByExample(example);
    }

    @Override
    public OnlineTestResult beginTesting(Long onlineTestId) {
        Example<OnlineTestResult> example = OnlineTestResult.createExample();
        example.createCriteria().andEqualTo("onlineTestId" , onlineTestId ).andEqualTo("status" , 0 );
        OnlineTestResult one = onlineTestResultService.findOneByExample(example);

        if(one == null ){
            OnlineTest onlineTest = findById(onlineTestId);

            example.clear();
            example.selectProperties("examPaperId");
            example.createCriteria().andEqualTo("onlineTestId" , onlineTestId );

            List<OnlineTestResult> list = onlineTestResultService.findListByExample(example);

            Set<Long> examPaperIds  = new HashSet<>();
            for(OnlineTestResult onlineTestResult : list ){
                examPaperIds.add(onlineTestResult.getExamPaperId());
            }

            ExamPaperQuestionsVo examPaper = null ;
            for(int i = 0 ; i < 10 ; i++ ){
                Long examPaperId = getOneExamPaperId(onlineTest, examPaperIds);
                if(examPaperId == null ){
                    throw new BusException("已经不能再测试了");
                }
                examPaper = examPaperService.findQuestionsOfExamPaper(examPaperId);
                if(examPaper == null ){
                    examPaperIds.add(examPaperId);
                }else {
                    break;
                }
            }

            if(examPaper == null ){
                throw new BusException("已经不能再测试了");
            }

            one = new OnlineTestResult();
            one.setAnswerDetail(StringUtils.EMPTY);
            one.setExamPaperId(examPaper.getExamPaper().getExamPaperId());
            one.setOnlineTestId(onlineTestId);
            one.setScore(0);
            one.setStatus(0);
            one.setTestDuration(0);
            one.setTestTime(TimeUtils.now());
            one.setCreateTime(TimeUtils.now());
            one.setUpdateTime(TimeUtils.now());
            one.setExamPaperMirror(JSONObject.toJSONString(examPaper, SerializerFeature.WriteNonStringValueAsString));
            onlineTestResultService.insertSelective(one);
        }

        return one;
    }

    private Long getOneExamPaperId(OnlineTest onlineTest, Set<Long> examPaperIds) {
        Long examPaperId = null ;
        for(Long id : Utils.toLongIds(onlineTest.getAvailableExamPaperIds())){
            if(!examPaperIds.contains(id)){
                examPaperId = id ;
            }
        }
        return examPaperId;
    }

    @Transactional
    @Override
    public void saveTestResult(Long onlineTestResultId, Integer testDuration, String answerDetail, Boolean isComplete) {

        if(testDuration <  0 ){
            OnlineTestResult preResult = onlineTestResultService.findById(onlineTestResultId);
            testDuration = preResult.getTestDuration();
            answerDetail = preResult.getAnswerDetail();
            isComplete = true ;
        }

        OnlineTestResult onlineTestResult = new OnlineTestResult();
        onlineTestResult.setOnlineTestResultId(onlineTestResultId);
        onlineTestResult.setTestDuration(testDuration);
        onlineTestResult.setAnswerDetail(answerDetail);
        onlineTestResult.setUpdateTime(TimeUtils.now());

        if(isComplete){

            OnlineTestResult result = onlineTestResultService.findById(onlineTestResultId);
            ExamPaperQuestionsVo examPaperQuestionsVo = JSONObject.parseObject(result.getExamPaperMirror(), ExamPaperQuestionsVo.class);
            Map<Long,ExamQuestion> examQuestionId2ExamQuestion = BeanUtils.toMap(examPaperQuestionsVo.getQuestions(),"examQuestionId");

            JSONArray results = JSONObject.parseArray(answerDetail);
            int totalScore = 0 ;
            for(int i = 0 ; i < results.size() ; i++ ){
                JSONObject jsonObject = results.getJSONObject(i);
                Long examQuestionId = jsonObject.getLong("examQuestionId");
                ExamQuestion examQuestion = examQuestionId2ExamQuestion.get(examQuestionId);
                if(examQuestion.getAnswer().replaceAll("\\s","").contains(jsonObject.getString("answer").replaceAll("\\s" ,""))){
                    totalScore += examQuestion.getScore();
                }
            }

            onlineTestResult.setScore(totalScore);
            onlineTestResult.setStatus(1);

            Example<OnlineTestResult> onlineTestResultExample = OnlineTestResult.createExample();
            onlineTestResultExample.createCriteria().andEqualTo("onlineTestResultId" , onlineTestResultId ).andEqualTo("status" , 0 );

            if( onlineTestResultService.updateSelectiveByExample(onlineTestResult,onlineTestResultExample ) ){
                writeScore(result.getOnlineTestId(), totalScore);
            }


        }else{

            onlineTestResultService.updateSelectiveById(onlineTestResult);
        }



    }

    private void writeScore(Long onlineTestId , int totalScore) {
        OnlineTest onlineTest = findById(onlineTestId);

        Measurement measurement = measurementService.findById(onlineTest.getMeasurementId(),"passingScore");


        if( onlineTest.getIsPass() == 0 && (totalScore * 10 ) >= (int)( measurement.getPassingScore() * 10 ) ){
            onlineTest.setIsPass(1);
            updateSelectiveById(onlineTest);
        }


        updateSelectiveById(onlineTest);

        Campus campus = campusService.findById(onlineTest.getCampusId() ,"areaTeamId" , "areaTeamName");
        StudentMeasureScoreSaveOrUpdateVo studentMeasureScoreSaveOrUpdateVo = new StudentMeasureScoreSaveOrUpdateVo();
        studentMeasureScoreSaveOrUpdateVo.setWay(1);
        studentMeasureScoreSaveOrUpdateVo.setAreaTeamId(campus.getAreaTeamId());
        studentMeasureScoreSaveOrUpdateVo.setAreaTeamName(campus.getAreaTeamName());
        studentMeasureScoreSaveOrUpdateVo.setCampusId(onlineTest.getCampusId());
        studentMeasureScoreSaveOrUpdateVo.setCampusName(onlineTest.getCampusName());
        studentMeasureScoreSaveOrUpdateVo.setGradeId(onlineTest.getGradeId());
        studentMeasureScoreSaveOrUpdateVo.setGradeName(onlineTest.getGradeName());
        studentMeasureScoreSaveOrUpdateVo.setSubjectId(onlineTest.getSubjectId());
        studentMeasureScoreSaveOrUpdateVo.setSubjectName(onlineTest.getSubjectName());
        studentMeasureScoreSaveOrUpdateVo.setValidity(1);
        studentMeasureScoreSaveOrUpdateVo.setStudentId(onlineTest.getStudentId());
        studentMeasureScoreSaveOrUpdateVo.setStudentScore(Double.valueOf( totalScore) );

        studentMeasureScoreService.saveOrUpdate(studentMeasureScoreSaveOrUpdateVo,null );
    }

    @Override
    public List<OnlineTest> findOnlineTestForTests(Long studentId) {
        Example<OnlineTest> example = OnlineTest.createExample();
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("studentId" , studentId );
        example.orderBy("status").asc().orderBy("createTime").desc();
        return findListByExample(example);
    }

    @Override
    @Transactional
    public void saveExternalScore(Long onlineTestId, Double score) {
        OnlineTest onlineTest = findById(onlineTestId);

        if(onlineTest.getExternalScoreType() == null || onlineTest.getExternalScoreType() == 0 ){
            return ;
        }

        Measurement measurement = measurementService.findById(onlineTest.getMeasurementId());
        Student student = studentService.findById(onlineTest.getStudentId());

        onlineTest.setExternalScoreUpdateTime(TimeUtils.now());
        onlineTest.setExternalScore(score);
        boolean isPass = false ;
        if( measurement.getExternalScoreType() == 2 && (int)( score * 10 ) <= (int)( measurement.getExternalPassingScore() * 10 ) ){
            isPass = true ;
        }else if(measurement.getExternalScoreType() != 2 && (int)( score * 10 ) >= (int)( measurement.getExternalPassingScore() * 10 )){
            isPass = true ;
        }

        onlineTest.setIsPass(isPass ? 1 : 0);
        updateSelectiveById(onlineTest);

        ExternalMeasureScore externalMeasureScore = new ExternalMeasureScore();
        externalMeasureScore.setGradeId(onlineTest.getGradeId());
        externalMeasureScore.setGradeName(onlineTest.getGradeName());
        externalMeasureScore.setSubjectId(onlineTest.getSubjectId());
        externalMeasureScore.setSubjectName(onlineTest.getSubjectName());
        externalMeasureScore.setValidity(1);
        externalMeasureScore.setStudentId(onlineTest.getStudentId());
        externalMeasureScore.setScoreType(onlineTest.getExternalScoreType());
        externalMeasureScore.setScore(score);
        externalMeasureScore.setStudentId(student.getStudentId());
        externalMeasureScore.setStudentName(student.getName());
        externalMeasureScore.setStudentPhoneNo(student.getPhoneNo());

        externalMeasureScore.setCreateTime(TimeUtils.now());
        externalMeasureScore.setUpdateTime(TimeUtils.now());
        externalMeasureScoreService.insertSelective(externalMeasureScore);


    }

    @Override
    public void mergeOnlineTest(long originStudentId, long targetStudentId) {
        Example<OnlineTest> example=OnlineTest.createExample();
        example.createCriteria().andEqualTo("studentId",originStudentId);
        List<OnlineTest> list=this.findListByExample(example);
        for (OnlineTest onlineTest:list){
            OnlineTest update=new OnlineTest();
            update.setOnlineTestId(onlineTest.getOnlineTestId());
            update.setStudentId(targetStudentId);
            update.setUpdateTime(TimeUtils.now());
            try{
                this.updateSelectiveById(update);//更新不成功，索引冲突，删除该记录。
            }catch (Exception e){
                this.deleteById(onlineTest.getOnlineTestId());
            }
        }
    }
}